package com.chatplus.application.client.pay;

import com.chatplus.application.client.pay.domain.request.wechat.WeChatRefundRequest;
import com.chatplus.application.client.pay.domain.response.wechat.WechatOrderPayResponse;
import com.chatplus.application.common.exception.BadRequestException;
import com.chatplus.application.common.logging.SouthernQuietLogger;
import com.chatplus.application.common.logging.SouthernQuietLoggerFactory;
import com.chatplus.application.config.properties.WechatPayProperties;
import com.chatplus.application.domain.entity.pay.PayRequestEntity;
import com.chatplus.application.domain.entity.pay.RefundRequestEntity;
import com.github.binarywang.wxpay.bean.notify.SignatureHeader;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyV3Result;
import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyV3Result;
import com.github.binarywang.wxpay.bean.request.WxPayOrderQueryV3Request;
import com.github.binarywang.wxpay.bean.request.WxPayRefundQueryV3Request;
import com.github.binarywang.wxpay.bean.request.WxPayRefundV3Request;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request;
import com.github.binarywang.wxpay.bean.result.WxPayOrderQueryV3Result;
import com.github.binarywang.wxpay.bean.result.WxPayRefundQueryV3Result;
import com.github.binarywang.wxpay.bean.result.WxPayRefundV3Result;
import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum;
import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

/**
 * description:
 *
 * @author Min
 * @date 22:58 - 2021/2/23.
 */
@Component
public class WeChatPayClient {

    private static final SouthernQuietLogger LOGGER = SouthernQuietLoggerFactory.getLogger(WeChatPayClient.class);

    private final WechatPayProperties wechatPayProperties;

    public WeChatPayClient(WechatPayProperties wechatPayProperties) {
        this.wechatPayProperties = wechatPayProperties;
    }

    private WxPayService wxPayService;

    public void initWxPayContextService() {
        if (!wechatPayProperties.isEnable()) {
            throw new BadRequestException("微信支付未开启");
        }
        wxPayService = new WxPayServiceImpl();
        WxPayConfig payConfig = new WxPayConfig();
        payConfig.setAppId(StringUtils.trimToNull(wechatPayProperties.getAppId()));
        payConfig.setMchId(StringUtils.trimToNull(wechatPayProperties.getMchId()));
        payConfig.setMchKey(StringUtils.trimToNull(wechatPayProperties.getMchKey()));
        payConfig.setNotifyUrl(StringUtils.trimToNull(wechatPayProperties.getNotifyUrl()));
        payConfig.setApiV3Key(StringUtils.trimToNull(wechatPayProperties.getApiV3Key()));
        payConfig.setPrivateKeyPath(StringUtils.trimToNull(wechatPayProperties.getPrivateKeyPath()));
        payConfig.setPrivateCertPath(StringUtils.trimToNull(wechatPayProperties.getPrivateCertPath()));
        wxPayService.setConfig(payConfig);
    }

    public WxPayNotifyV3Result parseOrderNotifyV3Result(String xmlData, HttpServletRequest request) throws WxPayException {
        initWxPayContextService();
        return wxPayService.parseOrderNotifyV3Result(xmlData, this.getSignatureHeader(request));
    }

    public WxPayRefundNotifyV3Result parseRefundNotifyV3Result(String xmlData, HttpServletRequest request) throws WxPayException {
        initWxPayContextService();
        return wxPayService.parseRefundNotifyV3Result(xmlData, this.getSignatureHeader(request));
    }

    /**
     * 获取回调请求头：签名相关
     * 文档地址：https://pay.weixin.qq.com/docs/merchant/development/interface-rules/signature-verification.html
     *
     * @param request HttpServletRequest
     * @return SignatureHeader
     */
    private SignatureHeader getSignatureHeader(HttpServletRequest request) {
        // 应答签名
        String signature = request.getHeader("Wechatpay-Signature");
        // 应答随机串
        String nonce = request.getHeader("Wechatpay-Nonce");
        // 检查平台证书序列号
        String serial = request.getHeader("Wechatpay-Serial");
        // 应答时间戳
        String timestamp = request.getHeader("Wechatpay-Timestamp");
        return SignatureHeader.builder()
                .nonce(nonce)
                .timeStamp(timestamp)
                .serial(serial)
                .signature(signature)
                .build();
    }
    /**
     * PC 支付
     */
    public WechatOrderPayResponse webPay(WxPayUnifiedOrderV3Request request) throws WxPayException {
        initWxPayContextService();
        Object createOrderResult = createOrderV3(wxPayService, TradeTypeEnum.NATIVE, request);
        LOGGER.message("微信支付-Web支付")
                .context("request", request)
                .context("result", createOrderResult)
                .info();
        WechatOrderPayResponse result = new WechatOrderPayResponse();
        result.setCodeUrl((String) createOrderResult);
        result.setMchid(wxPayService.getConfig().getMchId());
        result.setAppId(wxPayService.getConfig().getAppId());
        return result;
    }
    private <T> T createOrderV3(WxPayService wxPayService, TradeTypeEnum tradeType, WxPayUnifiedOrderV3Request request) throws WxPayException {
        T orderV3 = null;
        for (int i = 0; i < 3; i++) {
            try {
                orderV3 = wxPayService.createOrderV3(tradeType, request);
            } catch (WxPayException wxPayException) {
                String customErrorMsg = wxPayException.getCustomErrorMsg();
                if (customErrorMsg != null && customErrorMsg.contains("failed to respond")) {
                    LOGGER.message("提交微信支付连接异常")
                            .context("tradeType", tradeType)
                            .context("domain", request)
                            .exception(wxPayException)
                            .error();
                } else {
                    throw wxPayException;
                }
            }
        }
        return orderV3;
    }


    /**
     * 订单状态查询
     */
    public WxPayOrderQueryV3Result queryOrderPay(PayRequestEntity payRequestEntity) throws WxPayException {
        initWxPayContextService();
        WxPayOrderQueryV3Request request = new WxPayOrderQueryV3Request();
        request.setMchid(payRequestEntity.getPlatformChannelAccount());
        request.setOutTradeNo(payRequestEntity.getPayTransactionId() + "");
        return wxPayService.queryOrderV3(request);
    }
    /**
     * 退款申请
     */
    public WxPayRefundV3Result applyRefund(WeChatRefundRequest refundRequest) throws WxPayException {
        initWxPayContextService();
        WxPayRefundV3Request request = new WxPayRefundV3Request();
        WxPayRefundV3Request.Amount amount = new WxPayRefundV3Request.Amount();
        request.setOutRefundNo(refundRequest.getRefundApplySeqNo());
        request.setTransactionId(refundRequest.getTradeTransactionId());
        request.setOutTradeNo(refundRequest.getPayTransactionId());
        if (StringUtils.isNotEmpty(refundRequest.getRefundRemark())) {
            request.setReason(StringUtils.trimToEmpty(refundRequest.getRefundRemark()));
        }
        request.setNotifyUrl(refundRequest.getNotifyUrl());
        amount.setTotal(refundRequest.getTotalMoney().intValue());
        amount.setRefund(refundRequest.getRefundMoney().intValue());
        amount.setCurrency("CNY");
        request.setAmount(amount);

        WxPayRefundV3Result wxPayRefundV3Result = null;
        for (int i = 0; i < 3; i++) {
            try {
                wxPayRefundV3Result = wxPayService.refundV3(request);
            } catch (WxPayException wxPayException) {
                String customErrorMsg = wxPayException.getCustomErrorMsg();
                if (customErrorMsg != null && customErrorMsg.contains("failed to respond")) {
                    LOGGER.message("提交微信退款连接异常")
                            .context("domain", request)
                            .exception(wxPayException)
                            .error();
                } else {
                    throw wxPayException;
                }
            }
        }
        return wxPayRefundV3Result;
    }

    /**
     * 退款查询
     */
    public WxPayRefundQueryV3Result queryRefund(RefundRequestEntity refundRequest) throws WxPayException {
        initWxPayContextService();
        WxPayRefundQueryV3Request request = new WxPayRefundQueryV3Request();
        request.setOutRefundNo(refundRequest.getRefundApplyNo());
        return wxPayService.refundQueryV3(request);
    }
}
